package com.ccnf.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ccnf.core.bean.StatusCode;
import com.ccnf.core.bean.TokenParams;
import com.ccnf.core.exception.BizException;
import com.ccnf.core.exception.ErrorForwardException;
import com.ccnf.core.utils.ResultUtil;
import com.ccnf.core.utils.ShareCodeUtil;
import com.ccnf.core.utils.SystemClock;
import com.ccnf.model.ao.SmsAO;
import com.ccnf.model.entity.*;
import com.ccnf.model.enums.AuthModeEnum;
import com.ccnf.model.enums.AuthStatusEnum;
import com.ccnf.model.enums.RoleTypeEnum;
import com.ccnf.model.enums.UnionRoleEnum;
import com.ccnf.model.mapper.*;
import com.ccnf.model.vo.InviRecordVO;
import com.ccnf.model.vo.InvitationVO;
import com.ccnf.model.vo.ReportDataVO;
import com.ccnf.model.vo.ReportVO;
import com.ccnf.service.InvitationService;
import com.ccnf.service.MailService;
import com.ccnf.service.SaasService;
import lombok.Cleanup;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.mail.internet.MimeUtility;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.*;

@Service
public class InvitationServiceImpl extends ServiceImpl<InvitationMapper, Invitation> implements InvitationService {
    @Autowired
    private AuthorizationMapper authorizationMapper;
    @Autowired
    private InvitationRegisterMapper invitationRegisterMapper;
    @Autowired
    private ExpoUserMapper expoUserMapper;
    @Autowired
    private InvitationUserMapper invitationUserMapper;
    @Autowired
    private InvitationReportMapper invitationReportMapper;

    @Override
    public List<Invitation> listInvitationBySearch(TokenParams tokenParams, String searchContent, AuthStatusEnum authStatus, Integer demo) {
        return baseMapper.selectInvitationBySearch(tokenParams.getExpoId(), searchContent, authStatus, demo);
    }

    @Override
    public Map<String, Object> findInvitationDetailInfo(Long invitationId, TokenParams tokenParams) {
        Map<String, Object> data = new HashMap<>();
        data.put("info", getInvitation(invitationId));
        Long userId = tokenParams.getUserId();
        if (userId != null) {
            InvitationUser invitationUser = getInvitationUser(invitationId, userId);
            long currentTime = SystemClock.second();
            if (invitationUser == null) {
                invitationUser = new InvitationUser();
                invitationUser.setInvitationId(invitationId);
                invitationUser.setUserId(userId);
                invitationUser.setUnionRole(UnionRoleEnum.BROWSER);
                invitationUser.setBrowseNum(1);
                invitationUser.setBrowseTime(currentTime);
                invitationUserMapper.insert(invitationUser);
                data.put("union", getInvitationUser(invitationId, userId));
            } else {
                invitationUser.setBrowseTime(currentTime);
                invitationUser.setBrowseNum(invitationUser.getBrowseNum() + 1);
                invitationUserMapper.updateById(invitationUser);
                data.put("union", invitationUser);
            }
        }
        data.put("record", invitationReportMapper.selectInvitationRecord(invitationId));
        new Thread(() -> {
            InvitationReport invitationReport = invitationReportMapper.selectInvitationReportInfo(invitationId, SystemClock.zeroSecond());
            if (invitationReport == null) {
                invitationReport = new InvitationReport();
                invitationReport.setExpoId(tokenParams.getExpoId());
                invitationReport.setInvitationId(invitationId);
                invitationReport.setReportTime(SystemClock.zeroSecond());
                invitationReport.setBrowseNum(1);
                invitationReportMapper.insert(invitationReport);
            } else {
                invitationReport.setBrowseNum(invitationReport.getBrowseNum() + 1);
                invitationReportMapper.updateById(invitationReport);
            }
        }).start();
        return data;
    }

    @Override
    public void modifyInvitation(Long inviId, Invitation invitation) {
        invitation.setId(inviId);
        ResultUtil.validateSQL(baseMapper.updateById(invitation), "更新失败");
    }

    @Override
    @Transactional
    public String createInviAuthCode(Long inviId, TokenParams tokenParams) {
        Long expoId = tokenParams.getExpoId();
        Long userId = tokenParams.getUserId();
        ExpoUser expoUser = expoUserMapper.selectExpoUser(expoId, userId);
        if (!expoUser.getRoleType().equals(RoleTypeEnum.MASTER)) {
            throw new BizException(StatusCode.NO_PERMISSION, "权限不够");
        }
        Invitation invitation = getInvitation(inviId);
        if (!invitation.getExpoId().equals(expoId)) {
            throw new BizException("展会信息不匹配");
        }
        AuthStatusEnum authStatus = invitation.getAuthStatus();
        if (authStatus.equals(AuthStatusEnum.DONE)) {
            throw new BizException("邀请函已授权");
        }
        invitation.setAuthStatus(AuthStatusEnum.AWAIT);
        ResultUtil.validateSQL(baseMapper.updateById(invitation), "更新授权码失败");
        Authorization authorization = new Authorization();
        authorization.setExpoId(invitation.getExpoId());
        authorization.setInvitationId(inviId);
        authorization.setRoleType(RoleTypeEnum.EXHIBITOR);
        ResultUtil.validateSQL(authorizationMapper.insert(authorization), "生成角色授权失败");
        return ShareCodeUtil.idToCode(authorization.getId());
    }

    @Override
    @Transactional
    public void handleUserAuth(String authCode, Long expoId, Long userId) {
        Authorization authorization = authorizationMapper.selectById(ShareCodeUtil.codeToId(authCode));
        if (authorization == null) {
            throw new ErrorForwardException(704, "授权码不匹配");
        }
        Long inviId = authorization.getInvitationId();
        if (inviId != null && inviId != 0) {
            // 存在邀请函
            Invitation invitation = baseMapper.selectById(inviId);
            if (invitation == null) {
                throw new ErrorForwardException("邀请函不存在");
            }
            if (invitation.getAuthStatus().equals(AuthStatusEnum.DONE)) {
                throw new ErrorForwardException(702, "邀请函已授权");
            }
            if (invitation.getAuthStatus().equals(AuthStatusEnum.NONE)) {
                throw new ErrorForwardException(701, "该邀请函未进行授权");
            }
            invitation.setAuthStatus(AuthStatusEnum.DONE);
            baseMapper.updateById(invitation);

            InvitationUser invitationUser = new InvitationUser();
            invitationUser.setInvitationId(inviId);
            invitationUser.setUserId(userId);
            InvitationUser selectOne = invitationUserMapper.selectOne(new QueryWrapper<>(invitationUser));
            if (selectOne == null) {
                invitationUser.setUnionRole(UnionRoleEnum.LICENSOR);
                invitationUserMapper.insert(invitationUser);
            } else {
                UnionRoleEnum unionRole = selectOne.getUnionRole();
                if (unionRole.equals(UnionRoleEnum.CREATOR)) {
                    throw new ErrorForwardException(800, "自身不能授权邀请函操作");
                } else if (unionRole.equals(UnionRoleEnum.LICENSOR)) {
                    throw new ErrorForwardException(801, "你已经成功授权该邀请函，请进入管理！");
                }
                selectOne.setUnionRole(UnionRoleEnum.LICENSOR);
                invitationUserMapper.updateById(selectOne);
            }
        }
        authorization.setAuthStatus(AuthStatusEnum.DONE);
        authorization.setUserId(userId);
        authorization.setAuthTime(SystemClock.second());
        authorizationMapper.updateById(authorization);

        ExpoUser expoUser = new ExpoUser();
        expoUser.setExpoId(expoId);
        expoUser.setUserId(userId);
        ExpoUser select = expoUserMapper.selectOne(new QueryWrapper<>(expoUser));
        if (select == null) {
            expoUser.setRoleType(authorization.getRoleType());
            expoUserMapper.insert(expoUser);
        } else if (select.getRoleType().getRoleType() < authorization.getRoleType().getRoleType()) {
            select.setRoleType(authorization.getRoleType());
            expoUserMapper.updateById(select);
        }
    }

    private Invitation getInvitation(Long inviId) {
        Invitation invitation = baseMapper.selectById(inviId);
        ResultUtil.validateNull(invitation, "邀请函不存在");
        return invitation;
    }

    @Override
    public List<Invitation> listUserInviByUserId(TokenParams tokenParams) {
        return baseMapper.selectInvitationByUserId(tokenParams.getExpoId(), tokenParams.getUserId());
    }

    @Override
    public InvitationUser findInviUser(Long inviId, TokenParams tokenParams) {
        return getInvitationUser(inviId, tokenParams.getUserId());
    }

    @Override
    public List<Invitation> listAuthInvi(TokenParams tokenParams) {
        return baseMapper.selectAuthInvitationByExpoId(tokenParams.getExpoId(), tokenParams.getUserId());
    }

    @Override
    public void registerInvi(InvitationRegister invitationRegister) {
        Long inviId = invitationRegister.getInviId();
        Invitation invitation = baseMapper.selectById(inviId);
        ResultUtil.validateNull(invitation, "邀请函不存在");
        invitationRegisterMapper.insert(invitationRegister);
        handleInviRegisterReport(invitation.getExpoId(), inviId);
        Long id = invitationRegister.getId();
        new Thread(() -> {
            InvitationRegister invitationRegister1 = convertRegisterId(invitationRegister.getRegisterId());
            if (invitationRegister1 != null) {
                invitationRegister1.setId(id);
                invitationRegisterMapper.updateById(invitationRegister1);
            }
        }).start();
    }

    /**
     * 处理邀请函分享报表统计
     * @param inviId 邀请函ID
     */
    private void handleInviRegisterReport(Long expoId, Long inviId) {
        new Thread(() -> {
            InvitationReport invitationReport = invitationReportMapper.selectInvitationReportInfo(inviId, SystemClock.zeroSecond());
            if (invitationReport == null) {
                invitationReport = new InvitationReport();
                invitationReport.setExpoId(expoId);
                invitationReport.setReportTime(SystemClock.zeroSecond());
                invitationReport.setInvitationId(inviId);
                invitationReport.setRegisterNum(1);
                invitationReportMapper.insert(invitationReport);
            } else {
                invitationReport.setRegisterNum(invitationReport.getRegisterNum() + 1);
                invitationReportMapper.updateById(invitationReport);
            }
        }).start();
    }

    @Override
    public ReportVO findInvitationReport(Long invitationId, Integer dayNum, Integer reportType) {
        if (dayNum <= 0) {
            throw new BizException("天数有误");
        }
        long zeroTime = SystemClock.zeroSecond();
        long startTime = zeroTime - 86400 * dayNum;

        // 通过邀请函每日统计数据的报表数据
        ReportVO reportVO = invitationReportMapper.selectInvitationReport(invitationId, startTime, reportType);
        return getReportVO(zeroTime, startTime, reportVO);
    }

    @Override
    @Transactional
    public Long saveInvitation(Invitation invitation, TokenParams tokenParams) {
        invitation.setExpoId(tokenParams.getExpoId());
        baseMapper.insert(invitation);
        InvitationUser invitationUser = new InvitationUser();
        invitationUser.setInvitationId(invitation.getId());
        invitationUser.setUserId(tokenParams.getUserId());
        invitationUser.setUnionRole(UnionRoleEnum.CREATOR);
        invitationUserMapper.insert(invitationUser);
        return invitation.getId();
    }

    private InvitationUser getInvitationUser(Long inviId, Long userId) {
        InvitationUser invitationUser = new InvitationUser();
        invitationUser.setInvitationId(inviId);
        invitationUser.setUserId(userId);
        return invitationUserMapper.selectOne(new QueryWrapper<>(invitationUser));
    }

    @Override
    public void collectInvitation(Long inviId, Integer isCollect, TokenParams tokenParams) {
        Invitation invitation = baseMapper.selectById(inviId);
        ResultUtil.validateNull(invitation, "邀请函不存在");
        Long userId = tokenParams.getUserId();
        InvitationUser invitationUser = getInvitationUser(inviId, userId);
        if (invitationUser == null) {
            invitationUser = new InvitationUser();
            invitationUser.setUserId(userId);
            invitationUser.setInvitationId(inviId);
            invitationUser.setIsCollect(isCollect);
            if (isCollect == 1) {
                invitationUser.setCollectTime(SystemClock.second());
                handleInviCollectReport(tokenParams.getExpoId(), inviId);
            }
            invitationUserMapper.insert(invitationUser);
        } else if (!isCollect.equals(invitationUser.getIsCollect())) {
            if (isCollect == 1) {
                invitationUser.setCollectTime(SystemClock.second());
                handleInviCollectReport(tokenParams.getExpoId(), inviId);
            }
            invitationUser.setIsCollect(isCollect);
            invitationUserMapper.updateById(invitationUser);
        }
    }

    /**
     * 处理邀请函收藏报表统计
     * @param inviId 邀请函ID
     */
    private void handleInviCollectReport(Long expoId, Long inviId) {
        new Thread(() -> {
            InvitationReport invitationReport = invitationReportMapper.selectInvitationReportInfo(inviId, SystemClock.zeroSecond());
            if (invitationReport == null) {
                invitationReport = new InvitationReport();
                invitationReport.setExpoId(expoId);
                invitationReport.setReportTime(SystemClock.zeroSecond());
                invitationReport.setInvitationId(inviId);
                invitationReport.setCollectNum(1);
                invitationReportMapper.insert(invitationReport);
            } else {
                invitationReport.setCollectNum(invitationReport.getCollectNum() + 1);
                invitationReportMapper.updateById(invitationReport);
            }
        }).start();
    }

    @Override
    public void handleInvitationShare(Long expoId, Long shareId, Long userId) {
        InvitationUser invitationUser = invitationUserMapper.selectById(shareId);
        if (invitationUser != null && !invitationUser.getUserId().equals(userId)) {
            invitationUser.setClickCount(invitationUser.getClickCount() + 1);
            invitationUserMapper.updateById(invitationUser);
            handleInviShareReport(expoId, invitationUser.getInvitationId());
        }
    }

    /**
     * 处理邀请函分享报表统计
     * @param inviId 邀请函ID
     */
    private void handleInviShareReport(Long expoId, Long inviId) {
        new Thread(() -> {
            InvitationReport invitationReport = invitationReportMapper.selectInvitationReportInfo(inviId, SystemClock.zeroSecond());
            if (invitationReport == null) {
                invitationReport = new InvitationReport();
                invitationReport.setExpoId(expoId);
                invitationReport.setReportTime(SystemClock.zeroSecond());
                invitationReport.setInvitationId(inviId);
                invitationReport.setForwardNum(1);
                invitationReportMapper.insert(invitationReport);
            } else {
                invitationReport.setForwardNum(invitationReport.getForwardNum() + 1);
                invitationReportMapper.updateById(invitationReport);
            }
        }).start();
    }

    @Override
    public Invitation finInvitationBasicInfo(Long inviId) {
        return getInvitation(inviId);
    }

    /** 前端页面地址 */
    @Value("${font.indexUrl}")
    private String fontIndexUrl;
    @Autowired
    private SaasService saasService;

    @Override
    public List<String> sendInvitationSms(Long inviId, List<SmsAO> sms, TokenParams tokenParams) {
        Invitation invitation = baseMapper.selectById(inviId);
        ResultUtil.validateNull(invitation, "邀请函信息不存在");
        String link = null;
        try {
            link = URLEncoder.encode(fontIndexUrl + "detail?inviId=" + inviId, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String smsContent = "尊敬的客户，您收到邀请函《" + invitation.getCompanyName() + "》一封，请点开链接查看：" + link;
        List<String> failureList = new ArrayList<>();
        sms.forEach(item -> {
            String phone = item.getPhone();
            boolean isSuccess = saasService.saasSendSms(tokenParams.getExpoId(), phone, smsContent);
            if (!isSuccess) {
                failureList.add(phone);
            }
        });
        return failureList;
    }

    @Override
    public InvitationReport findInvitationRecord(Long invitationId, Integer recordType) {
        if (recordType == null || recordType == 0) {
            return invitationReportMapper.selectInvitationRecord(invitationId);
        } else {
            return invitationReportMapper.selectInvitationReportInfo(invitationId, SystemClock.zeroSecond());
        }
    }

    @Override
    public List<Invitation> listInvitationCollect(TokenParams tokenParams) {
        return invitationUserMapper.selectInvitationCollectList(tokenParams.getExpoId(), tokenParams.getUserId());
    }

    @Override
    public List<InvitationVO> listInvitationRecordRank(Integer rankType, TokenParams tokenParams) {
        return invitationReportMapper.selectInvitationRecordList(tokenParams.getExpoId(), rankType);
    }

    @Override
    public InviRecordVO findInviCountRecord(Integer recordType, TokenParams tokenParams) {
        return invitationReportMapper.selectInviCountRecord(tokenParams.getExpoId(), recordType, SystemClock.zeroSecond());
    }

    @Override
    public ReportVO findInviCountReport(Integer dayNum, Integer reportType, TokenParams tokenParams) {
        if (dayNum <= 0) {
            throw new BizException("天数有误");
        }
        long zeroTime = SystemClock.zeroSecond();
        long startTime = zeroTime - 86400 * dayNum;

        // 通过邀请函每日统计数据的报表数据
        ReportVO reportVO = invitationReportMapper.selectInvitationCountReport(tokenParams.getExpoId(), startTime, reportType);
        return getReportVO(zeroTime, startTime, reportVO);
    }

    @Autowired
    private MailService mailService;

    @Override
    public void handleInviDataExport(TokenParams tokenParams, String email, Long inviId) {
        try {
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd");
            String date = simpleDateFormat.format(new Date());
            InvitationRegister invitationRegister = new InvitationRegister();
            invitationRegister.setInviId(inviId);
            List<InvitationRegister> invitationRegisters = invitationRegisterMapper.selectList(new QueryWrapper<>(invitationRegister));

            ExportParams params = new ExportParams();
            Workbook workbook = ExcelExportUtil.exportExcel(params, InvitationRegister.class, invitationRegisters);
            String fileName = MimeUtility.encodeText(date + ".xls");
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            workbook.write(outputStream);
            mailService.exportAttachment("邀请函数据(" + date + ")", email,
                    "请下载附件文件进行查看！", fileName, outputStream.toByteArray());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void batchImportInvi(Long expoId, List<Invitation> invitationList) {
        invitationList.forEach(invitation -> {
            invitation.setExpoId(expoId);
            baseMapper.insert(invitation);
        });
    }

    @Override
    public void batchImportAuth(Long expoId, List<Authorization> authorizationList) {
        authorizationList.forEach(authorization -> {
            authorization.setExpoId(expoId);
            authorization.setAuthMode(AuthModeEnum.SMS);
            authorization.setRoleType(RoleTypeEnum.EXHIBITOR);
            authorizationMapper.insert(authorization);
        });
    }

    private ReportVO getReportVO(long zeroTime, long startTime, ReportVO reportVO) {
        if (reportVO == null) {
            reportVO = new ReportVO();
        }
        List<ReportDataVO> reports = reportVO.getReportData();
        Integer maxValue = 0;
        int size = reports.size();
        if (size == 0) {
            // 没有报表数据
            while (startTime <= zeroTime) {
                ReportDataVO empty = new ReportDataVO();
                empty.setReportNum(0);
                empty.setReportTime(startTime);
                reports.add(empty);
                startTime += 86400;
            }
        } else {
            int i = 0;
            while (startTime <= zeroTime) {
                if (size <= i || startTime != reports.get(i).getReportTime()) {
                    ReportDataVO empty = new ReportDataVO();
                    empty.setReportNum(0);
                    empty.setReportTime(startTime);
                    reports.add(i, empty);
                    size++;
                } else {
                    Integer reportNum = reports.get(i).getReportNum();
                    if (reportNum > maxValue) {
                        maxValue = reportNum;
                    }
                }
                i++;
                startTime += 86400;
            }
        }
        reportVO.setMaxValue(maxValue);
        return reportVO;
    }

    /**
     * 通过登记ID将登记信息补充完整到数据库中
     * @param registerId 登记ID
     * @return
     */
    private InvitationRegister convertRegisterId(String registerId) {
        String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; // 加载JDBC驱动
        String dbURL = "jdbc:sqlserver://114.55.141.112;database=DBESP";
        String userName = "ngig"; // 用户名
        String userPwd = "ccnf@2016New"; // 密码
        try {
            Class.forName(driverName);
            @Cleanup Connection conn = DriverManager.getConnection(dbURL, userName, userPwd);
            PreparedStatement ps = conn.prepareStatement("select a.ugid,a.Name username,a.Phone phone,a.Title title,a.Company company, " +
                    "a.Address address,a.website,a.Tel tel,a.Email email,b.CName country,c.PName province,d.cName city, " +
                    "       a.TicketType ticketType " +
                    "from dbo.TB_Ticket a left join dbo.TB_Country b on a.Country=b.CCode left join dbo.TB_Province c\n" +
                    "on a.Province=c.PCode left join dbo.TB_City d on a.city=d.CCode where a.exid='1122'" +
                    "and a.cBarcode = ?");
            ps.setString(1, registerId);
            ResultSet resultSet = ps.executeQuery();
            while (resultSet.next()) {
                InvitationRegister invitationRegister = new InvitationRegister();
                String ugid = resultSet.getString("ugid");
                String username = resultSet.getString("username");
                String phone = resultSet.getString("phone");
                String title = resultSet.getString("title");
                String company = resultSet.getString("company");
                String address = resultSet.getString("address");
                String website = resultSet.getString("website");
                String tel = resultSet.getString("tel");
                String email = resultSet.getString("email");
                String country = resultSet.getString("country");
                String province = resultSet.getString("province");
                String city = resultSet.getString("city");
                String ticketType = resultSet.getString("ticketType");
                invitationRegister.setUgid(ugid);
                invitationRegister.setUsername(username);
                invitationRegister.setPhone(phone);
                invitationRegister.setTitle(title);
                invitationRegister.setCompany(company);
                invitationRegister.setAddress(address);
                invitationRegister.setWebsite(website);
                invitationRegister.setTel(tel);
                invitationRegister.setEmail(email);
                invitationRegister.setCountry(country);
                invitationRegister.setProvince(province);
                invitationRegister.setCity(city);
                invitationRegister.setTicketType(ticketType);
                return invitationRegister;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}